AIR for Androidでネイティブ拡張を試しました その3
AIR for Androidでネイティブ拡張を試しました その2 の続きです。
今回はネイティブの音声認識を利用したサンプルを作成します。 ソースは前回作ったものを使います。
まずはネイティブ側の実装です。 その1で作ったSampleFREExtensionを以下のように修正します 前回と違う点はファンクションの名前とstatic変数にFREContextを格納しているところです。
package sample.ane; import java.util.HashMap; import java.util.Map; import com.adobe.fre.FREContext; import com.adobe.fre.FREExtension; import com.adobe.fre.FREFunction; public class SampleFREExtension implements FREExtension { public static FREContext context; @Override public FREContext createContext(String arg0) { if(context == null) { context = new FREContext() { @Override public Map<String, FREFunction> getFunctions() { Map<String, FREFunction> result = new HashMap<String, FREFunction>(); result.put("recognizeSpeech", new MyFREFunction()); return result; } @Override public void dispose() {} }; } return context; } @Override public void dispose() {} @Override public void initialize() {} }
次にMyFREFunctionのcallメソッド内を以下のように修正します。 処理としてはSampleActivityというアクティビティを呼び出しているだけになります。
package sample.ane; import android.app.Activity; import android.content.Intent; import com.adobe.fre.FREContext; import com.adobe.fre.FREFunction; import com.adobe.fre.FREObject; import com.adobe.fre.FREWrongThreadException; public class MyFREFunction implements FREFunction { @Override public FREObject call(FREContext arg0, FREObject[] arg1) { try { Activity activity = arg0.getActivity(); Intent intent = new Intent(activity, SampleActivity.class); activity.startActivity(intent); return null; } catch (Exception e) { try { return FREObject.newObject(e.toString()); } catch (FREWrongThreadException e1) {} } return null; } }
次にcallメソッド内で呼び出しているSampleActivityを実装します。 onCreate内で暗黙的なIntentで音声認識の画面に遷移します。 結果をonActivityResult内で取得して検索結果を格納したStatusEventをFREContextにディスパッチします。 イベントをディスパッチしたらアクティビティを終了します。
実装が完了したら前回と同じようにjarファイルを作成して下さい。
package sample.ane; import java.util.List; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.speech.RecognizerIntent; public class SampleActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM); intent.putExtra(RecognizerIntent.EXTRA_PROMPT, "SampleActivity"); startActivityForResult(intent, 0); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == 0 && resultCode == RESULT_OK) { List<String> results = data.getStringArrayListExtra( RecognizerIntent.EXTRA_RESULTS); SampleFREExtension.context.dispatchStatusEventAsync(results.get(0), "result"); } super.onActivityResult(requestCode, resultCode, data); finish(); } }
次にネイティブ拡張を修正します。 前回とは違いEventDispatcherをスーパークラスにしています。これはネイティブ側から受け取ったStatusEventをディスパッチするためです。 recognizeSpeechファンクションの戻り値はエラーが発生した場合のログ取得用で音声検索の結果はStatusEventから取得します。
実装できたら前回と同じ方法でANEファイルを作成します。
package sample.ane { import flash.events.EventDispatcher; import flash.events.StatusEvent; import flash.external.ExtensionContext; public class SampleExtension extends EventDispatcher { private var context:ExtensionContext; public function SampleExtension() { context = ExtensionContext.createExtensionContext( "sample", "type"); } public function recognizeSpeech():String { context.addEventListener(StatusEvent.STATUS, statusHandler); return context.call("recognizeSpeech") as String; } private function statusHandler(event:StatusEvent):void { dispatchEvent(event); } public function dispose() : void { return context.dispose(); } } }
最後にAIRのソースコードです。 ボタンを押すと音声認識の画面に遷移します。 そこで何か音声を入力すると検索結果がTextAreaに表示されます。
<?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" applicationDPI="160"> <fx:Script> <![CDATA[ import sample.ane.SampleExtension; private var extension:SampleExtension; protected function button1_clickHandler(event:MouseEvent):void { extension = new SampleExtension(); extension.addEventListener(StatusEvent.STATUS, statusHandler); extension.recognizeSpeech(); } private function statusHandler(event:StatusEvent):void { textArea.text = event.code; } ]]> </fx:Script> <s:VGroup width="100%" height="100%"> <s:Button width="100%" label="Recognize Speech" click="button1_clickHandler(event)"/> <s:TextArea id="textArea" width="100%" height="100%"/> </s:VGroup> </s:Application>
最後にActivityを追加した場合はネイティブアプリ同様Activityを登録する必要があります。 アプリケーション記述ファイル(*-app.xml)の下の方にパーミッションなどを設定している箇所があるので、 以下のように修正します。
<android> <colorDepth>16bit</colorDepth> <manifestAdditions><![CDATA[ <manifest android:installLocation="auto"> <!--See the Adobe AIR documentation for more information about setting Google Android permissions--> <!--android.permission.INTERNET 権限を削除すると、 デバイス上でアプリケーションをデバッグできなくなります--> <uses-permission android:name="android.permission.INTERNET"/> <!--<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>--> <!--<uses-permission android:name="android.permission.READ_PHONE_STATE"/>--> <!--<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>--> <!--AIR の SystemIdleMode API にアクセスするには、DISABLE_KEYGUARD 権限と WAKE_LOCK 権限を同時に切り替える必要があります--> <!--<uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>--> <!--<uses-permission android:name="android.permission.WAKE_LOCK"/>--> <!--<uses-permission android:name="android.permission.CAMERA"/>--> <!--<uses-permission android:name="android.permission.RECORD_AUDIO"/>--> <!--AIR の NetworkInfo API を使用するには、ACCESS_NETWORK_STATE 権限と ACCESS_WIFI_STATE 権限を同時に切り替える必要があります--> <!--<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>--> <!--<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>--> <application> <activity> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <activity android:name="sample.ane.SampleActivity"></activity> </application> </manifest> ]]></manifestAdditions> </android>